#include "stdafx.h"
#include "RecursiveMutex.h"
#include <assert.h>
#ifdef WIN32
# include <Windows.h>
#else
# include <errno.h>
# include <utility/TimeSpec.h>
#endif

namespace serika
{
	namespace threading
	{
		RecursiveMutex::RecursiveMutex()
		{
	#ifdef WIN32
			mMutex = ::CreateMutex(0, FALSE, 0);
	#else
			pthread_mutexattr_t attr;
			pthread_mutexattr_init(&attr);
			pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
			pthread_mutex_init(&mMutex, &attr);
			pthread_mutexattr_destroy(&attr);
	#endif
		}
	
		RecursiveMutex::~RecursiveMutex()
		{
	#ifdef WIN32
			::CloseHandle(mMutex);
	#else
			pthread_mutex_destroy(&mMutex);
	#endif
		}
	
		LockState::Enum RecursiveMutex::Acquire(long timeout)
		{
	#ifdef WIN32
			DWORD ret = ::WaitForSingleObject(mMutex, timeout);
			switch (ret)
			{
			case WAIT_OBJECT_0:
				return LockState::SUCCEEDED;
			case WAIT_TIMEOUT:
				return LockState::TIMEDOUT;
			case WAIT_ABANDONED:
				return LockState::OWNERTERM;
			default:
				return LockState::FAILED;
			}
	#else
			int ret = 0;
			if (timeout == -1)
			{
				ret = pthread_mutex_lock(&mMutex);
			}
			else
			{
				ut::TimeSpec t = ut::TimeSpec::MakeTimeout(timeout);
#if defined(__APPLE__) && defined(__MACH__)
				// unsupport
				assert(false);
				ret = pthread_mutex_lock(&mMutex);
#else
				ret = pthread_mutex_timedlock(&mMutex, &t);
#endif
			}
			switch (ret)
			{
			case 0:
				return LockState::SUCCEEDED;
			case ETIMEDOUT:
				return LockState::TIMEDOUT;
			default:
				return LockState::FAILED;
			}
	#endif
		}
	
		LockState::Enum RecursiveMutex::TryAcquire()
		{
	#ifdef WIN32
			DWORD ret = ::WaitForSingleObject(mMutex, 0);
			switch (ret)
			{
			case WAIT_OBJECT_0:
				return LockState::SUCCEEDED;
			case WAIT_TIMEOUT:
				return LockState::BUSY;
			case WAIT_ABANDONED:
				return LockState::OWNERTERM;
			default:
				return LockState::FAILED;
			}
	#else
			int ret = pthread_mutex_trylock(&mMutex);
			switch (ret)
			{
			case 0:
				return LockState::SUCCEEDED;
			case EBUSY:
				return LockState::BUSY;
			case ETIMEDOUT:
				return LockState::TIMEDOUT;
			default:
				return LockState::FAILED;
			}
	#endif
		}
	
		void RecursiveMutex::Release()
		{
	#ifdef WIN32
			::ReleaseMutex(mMutex);
	#else
			pthread_mutex_unlock(&mMutex);
	#endif
		}
	}
}
